﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Cryptography;

namespace FolderComparer2
{
    public class FolderComparerClass
    {
        static public bool Compare(string firstfolderpath, string secondfolderpath)
        {
            IEnumerable<FileInfo> Folder1 = GetFiles(firstfolderpath);
            IEnumerable<FileInfo> Folder2 = GetFiles(secondfolderpath);

            //I stored each file title and hached content within two dictioanries, each dictionary correponds to a given folder
            Dictionary<byte[], string> Dictionary1 = new Dictionary<byte[], string>();
            Dictionary<byte[], string> Dictionary2 = new Dictionary<byte[], string>();

            foreach (var item in Folder1)
            {
                byte[] filecontentHach = FileContentHach(item.OpenRead());
                string fileTitle = item.Name;
                Dictionary1.Add(filecontentHach, fileTitle);
            }

            foreach (var item in Folder2)
            {
                byte[] filecontentHach = FileContentHach(item.OpenRead());
                string fileTitle = item.Name;
                Dictionary2.Add(filecontentHach, fileTitle);
            }
            bool flag = CompareFolders(Dictionary1, Dictionary2);
            return flag;

        }
        /// <summary>
        /// This method could be fusioned with CompareTwoDictionaries method, but I did separate them
        /// by purpose to get things more clear for you, anyway this code needs to b refactored
        /// </summary>
        /// <param name="Dictionary1">contains informations about the folder1</param>
        /// <param name="Dictionary2">contains informations about the folder2</param>
        /// <returns></returns>
        static bool CompareFolders(Dictionary<byte[], string> Dictionary1, Dictionary<byte[], string> Dictionary2)
        {
            bool flag = false;
            flag = (Dictionary1.Count == Dictionary2.Count) ? true : false;
            if (flag == true)
            {
                flag = CompareTwoDictionaries(Dictionary1, Dictionary2);
            }
            return flag;
        }
        /// <summary>
        /// This method  will compare two dictionaries, both dictionaries contains couples
        /// each couple is composed by the title of a given file and the correponding hach result of its content
        /// </summary>
        /// <param name="Dictionary1">contains informations about the folder1</param>
        /// <param name="Dictionary2">contains informations about the folder2</param>
        /// <returns>returns a flag that indicates if the content of the two dictionaries are the same or not</returns>
        static bool CompareTwoDictionaries(Dictionary<byte[], string> Dictionary1, Dictionary<byte[], string> Dictionary2)
        {
            bool flag = true;
            int count = Dictionary1.Count;

            string[] values1 = Dictionary1.Values.ToArray();
            string[] values2 = Dictionary2.Values.ToArray();

            byte[][] keys1 = Dictionary1.Keys.ToArray();
            byte[][] keys2 = Dictionary2.Keys.ToArray();

            for (int i = 0; i < count; i++)
            {
                if (!values1[0].Equals(values2[0]))
                {
                    flag = false;
                    break;
                }
            }
            if (flag == true)
            {
                for (int i = 0; i < count; i++)
                {
                    if (!CompareByteArrays(keys1[i], keys2[i]))
                    {
                        flag = false;
                        break;
                    }
                }
            }

            return flag;
        }


#if test 
        static IEnumerable<FileInfo> GetFiles(string filepath)
        {
            DirectoryInfo directoryInfo = new DirectoryInfo(filepath);
            return directoryInfo.GetFiles();
        }
#else
        /// <summary>
        /// This method recieves the file paths and generates a set
        /// of file streams correponding to those file paths, this method
        /// needs some extra work to avoid the case were files
        /// do no exist within the file system, so I let this homework to you ;)
        /// </summary>
        /// <param name="filepaths"></param>
        /// <returns>An IEnumerable of FileInfo objects</returns>
        static IEnumerable<FileInfo> GetFiles(params string[] filepaths)
        {
            foreach (var item in filepaths)
            {
                yield return new FileInfo(item);
            }
        }
#endif

        /// <summary>
        /// This method will generate a unique key a byte array
        /// that will varies depending on the file content
        /// </summary>
        /// <param name="stream">This is the file going to have the hach code</param>
        /// <returns>the byte array that corresponds to the unique key of the given file</returns>
        static byte[] FileContentHach(FileStream stream)
        {
            MD5 hachAlgo = MD5.Create();
            /*You can use Hash to calculate a hash of the entire file
             or computehash to calculate a hash or a partial part
             of the file if you're wooried about optimization*/
            byte[] filehash = hachAlgo.ComputeHash(stream);
            hachAlgo.Clear();
            hachAlgo.Dispose();
            return filehash;
        }

        /// <summary>
        /// This method compare two bytes arrays element by element or byte by byte
        /// </summary>
        /// <param name="array1">array of bytes</param>
        /// <param name="array2">array of bytes</param>
        /// <returns>boolean that indicates if two bytes arrays have the same content</returns>
        static bool CompareByteArrays(byte[] array1, byte[] array2)
        {
            /*This flag will indicate if two arrays have the same content or not*/
            bool flag = true;
            /* If the two arrays don't have the same count they
             don't  have the same content automatically*/
            if (array1.Length != array2.Length)
            {
                flag = false;
            }
            /*If they have the same count then we have to compare their
             contents byte by byte*/
            for (int i = 0; i < array1.Length; i++)
            {
                if (array1[i].CompareTo(array2[i]) != 0)
                {
                    flag = false;
                    break;
                }
            }
            return flag;
        }
    }
}
